package com.techfied.hex;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.dnd.DropTarget;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.ScrollPaneLayout;
import javax.swing.SwingConstants;
import javax.swing.border.MatteBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

public class HexPane extends JScrollPane implements MouseWheelListener {

	public static final int Nc = 18;
	public static final int Nr = 30;

	private static MyDragDropListener myDragDropListener = new MyDragDropListener();
	public List<TableModel> items = new ArrayList<TableModel>();
	private RandomAccessFile fileStream;

	public static int chunksize = Nc * Nr;
	public final JTable file;

	public static final int APPEND = 1;
	public static final int PREPEND = 2;
	private int lastreadcount = 0;
	private int lastDirection = 0;

	public HexPane(JTable pane) {

		super(pane);
		this.file = pane;
		this.addMouseWheelListener(this);

		file.setBackground(Color.WHITE);
		file.setGridColor(Color.getHSBColor(240, 240, 240));
		file.setRowSelectionAllowed(false);
		ListSelectionModel selModel = file.getSelectionModel();
		selModel.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);

		file.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
			@Override
			public Component getTableCellRendererComponent(JTable table,
					Object value, boolean isSelected, boolean hasFocus,
					int row, int column) {
				JLabel cell = (JLabel) super.getTableCellRendererComponent(
						table, value, isSelected, hasFocus, row, column);
				cell.setHorizontalAlignment(SwingConstants.CENTER);
				cell.setVerticalAlignment(SwingConstants.CENTER);
				TableModel model = (TableModel) table.getModel();
				Cell val = (Cell) model.getValueAt(row, column);

				if (val != null && val.isSelected) {
					cell.setBackground(Color.YELLOW);
				} else if (val != null && val.value == 0x00) {
					cell.setBackground(Color.WHITE);
				} else {
					cell.setBackground(Color.WHITE);
				}
				return cell;
			}
		});

		file.addMouseListener(new MouseListener() {

			@Override
			public void mouseReleased(MouseEvent e) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mousePressed(MouseEvent e) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mouseExited(MouseEvent e) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mouseEntered(MouseEvent e) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mouseClicked(MouseEvent e) {
				TableModel tm = (TableModel) file.getModel();
				int i = file.getSelectedRow() * HexPane.Nc
						+ file.getSelectedColumn();
				String message = "Offset: " + i;
				if (tm.data != null && tm.data.size() > i) {
					message += ", Hex: " + tm.data.get(i);
					message += ", Dec: " + tm.data.get(i).value;
				}
				EntryPointWindow.statusLabel.setText(message);
				EntryPointWindow.focusCell(file);
			}
		});

		int width = (2 * EntryPointWindow.fontSize) * Nc;
		int height = (EntryPointWindow.fontSize + 5) * Nr;

		new DropTarget(this, myDragDropListener);
		this.setLayout(new ScrollPaneLayout());
		this.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
		this.setPreferredSize(new Dimension(width, height));

	}

	public void clear() {
		items.clear();
		file.removeAll();
	}

	public void update() {
		file.updateUI();
	}

	public void setFile(File f) {
		try {
			fileStream = new RandomAccessFile(f, "r");

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public int read(int count, int mode) throws IOException {
		if (fileStream == null) {
			return 0;
		}
		byte[] data = new byte[count];
		int size = fileStream.read(data);
		if (size > 0 && mode == APPEND) {
			getModel().append(data);
		} else {
			getModel().prepend(data);
		}
		return size;
	}

	public int count() {
		return getModel().data.size();
	}

	public void highlight(int i) {

		Cell cell = (Cell) getModel().getValueAt(i / Nc, i % Nc);
		cell.isSelected = true;
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent event) {

		if (event.getWheelRotation() > 0) {
			EntryPointWindow.scroll(1);
		} else {
			EntryPointWindow.scroll(-1);
		}
	}

	public void moveForward() throws IOException {
		if (fileStream == null) {
			return;
		}
		long offset = fileStream.getFilePointer();
		TableModel tm = getModel();

		if (lastDirection == 0) {
			offset += HexPane.Nc;
		} else if (lastDirection > 0) {
			offset += HexPane.Nc;
		} else if (lastDirection < 0) {
			offset += tm.getSize();
			fileStream.seek(offset);
		}
		lastreadcount = read(HexPane.Nc, APPEND);
		lastDirection = 1;
		System.out.println("off>: " + offset + " tm:" + tm.getSize());
	}

	public void moveBackward() throws IOException {
		if (fileStream == null) {
			return;
		}
		long offset = fileStream.getFilePointer();
		TableModel tm = getModel();
		if (lastDirection == 0) {
			offset = offset - (tm.getSize() + HexPane.Nc);
		} else if (lastDirection > 0) {
			offset = offset - (tm.getSize() + HexPane.Nc);
		} else if (lastDirection < 0) {
			offset -= 2 * HexPane.Nc;
		}

		if (offset >= 0) {
			fileStream.seek(offset);
			lastreadcount = read(HexPane.Nc, PREPEND);
			lastDirection = -1;
		}
		System.out.println("off<: " + offset + " tm:" + tm.getSize());
	}

	public byte getData(int i) {
		Cell cell = getModel().data.get(i);
		return cell.value;
	}

	public void reset() throws IOException {
		if (fileStream != null)
			fileStream.seek(0);
		getModel().data.clear();
		this.read(HexPane.chunksize, HexPane.APPEND);
		this.update();
	}

	public TableModel getModel() {
		return (TableModel) file.getModel();
	}
}
